<!--
/**
 * author: vformAdmin
 * email: vdpadmin@163.com
 * website: https://www.vform666.com
 * date: 2021.08.18
 * remark: 如果要分发VForm源码，需在本文件顶部保留此文件头信息！！
 */
-->

<template>
  <el-form
    :label-position="labelPosition"
    :size="size"
    :rules="rules"
    :class="[customClass, readModeFlag ? 'readonly-mode-form' : '']"
    class="render-form"
    :label-width="labelWidth"
    :validate-on-rule-change="false"
    :scroll-to-error="true"
    :model="formDataModel"
    ref="renderForm"
    @submit.enter.prevent
    v-if="widgetList && widgetList.length > 0"
  >
  <!-- modify by weig 2025-02-12 修复是空表单的情况下还出现滚动条的bug -->
    <template v-for="(widget, index) in widgetList">
      <template v-if="'container' === widget.category">
        <component
          :is="getContainerWidgetName(widget)"
          :widget="widget"
          :key="widget.id"
          :parent-list="widgetList"
          :index-of-parent-list="index"
          :parent-widget="null"
        >
          <!-- 递归传递插槽！！！ -->
          <template v-for="slot in Object.keys($slots)" v-slot:[slot]="scope">
            <slot :name="slot" v-bind="scope" />
          </template>
        </component>
      </template>
      <template v-else>
        <component
          :is="getWidgetName(widget)"
          :field="widget"
          :form-model="formDataModel"
          :designer="null"
          :key="widget.id"
          :parent-list="widgetList"
          :index-of-parent-list="index"
          :parent-widget="null"
        >
          <!-- 递归传递插槽！！！ -->
          <template v-for="slot in Object.keys($slots)" v-slot:[slot]="scope">
            <slot :name="slot" v-bind="scope" />
          </template>
        </component>
      </template>
    </template>
  </el-form>
  <div class="empty" v-else>
    <!-- <img class="no-data-img" :alt="i18nt('designer.setting.noData')" src="../../../basic/assets/image/card-no-data.svg" /> -->
    <div class="empty-text">{{ i18nt('designer.setting.noData') }}</div>
  </div>
</template>

<script>
import formDesigner from '@common/form-designer';
import core from '@common/core';
import basic from '@common/basic';

// import VFormRender from '../../components/form-render/index.vue';
const VFormRender = formDesigner.getExportResource('src/form-designer/components/form-render/index.vue').default;

// import print from '@/form-designer/utils/print.js';
const  print  = formDesigner.getExportResource(
  'src/form-designer/utils/print.js'
);

// import request from '@/core/utils/http-tools.js';
const request = core.getExportResource('src/core/utils/http-tools.js').default;
import { createVNode, render } from 'vue';

// import {
//   deepClone,
//   getAllFieldWidgets,
//   getAllContainerWidgets,
//   traverseFieldWidgetsOfContainer,
//   hasPropertyOfObject,
//   getObjectValue,
//   setObjectValue,
//   generateId,
//   getContainerWidgetByName,
//   cloneFormConfigWithoutEventHandler,
//   getCurUser,
//   traverseFieldWidgetsNew
// } from '@/form-designer/utils/util';
// const {
//   deepClone,
//   getAllFieldWidgets,
//   getAllContainerWidgets,
//   traverseFieldWidgetsOfContainer,
//   hasPropertyOfObject,
//   getObjectValue,
//   setObjectValue,
//   generateId,
//   getContainerWidgetByName,
//   cloneFormConfigWithoutEventHandler,
//   getCurUser,
//   traverseFieldWidgetsNew
// }  = formDesigner.getExportResource(
//   'src/form-designer/utils/util.js'
// );
const { deepClone }  = formDesigner.getExportResource(
  'src/form-designer/utils/util.js'
);
const { getAllFieldWidgets }  = formDesigner.getExportResource(
  'src/form-designer/utils/util.js'
);
const { getAllContainerWidgets }  = formDesigner.getExportResource(
  'src/form-designer/utils/util.js'
);
const { traverseFieldWidgetsOfContainer }  = formDesigner.getExportResource(
  'src/form-designer/utils/util.js'
);
const { hasPropertyOfObject }  = formDesigner.getExportResource(
  'src/form-designer/utils/util.js'
);
const { getObjectValue }  = formDesigner.getExportResource(
  'src/form-designer/utils/util.js'
);
const { setObjectValue }  = formDesigner.getExportResource(
  'src/form-designer/utils/util.js'
);
const { generateId }  = formDesigner.getExportResource(
  'src/form-designer/utils/util.js'
);
const { getContainerWidgetByName }  = formDesigner.getExportResource(
  'src/form-designer/utils/util.js'
);
const { cloneFormConfigWithoutEventHandler }  = formDesigner.getExportResource(
  'src/form-designer/utils/util.js'
);
const { getCurUser }  = formDesigner.getExportResource(
  'src/form-designer/utils/util.js'
);
const { traverseFieldWidgetsNew }  = formDesigner.getExportResource(
  'src/form-designer/utils/util.js'
);

// import { formatDate } from '@/basic/utils/index.js';
const {formatDate} = basic.getExportResource('src/basic/utils/index.js').default;

// import { 
//   getcascadConditionMap, 
//   getConditionMap, 
//   mergeActions, 
//   applyAttribute, 
//   resetActions, 
//   resetCascadeActions,
//   handleFilterRuleValueObj,
//   judgeExpress
// } from '@/form-designer/utils/businessRules.js';
const { 
  getcascadConditionMap, 
  getConditionMap, 
  mergeActions, 
  applyAttribute, 
  resetActions, 
  resetCascadeActions,
  handleFilterRuleValueObj,
  judgeExpress
} = formDesigner.getExportResource(
  'src/form-designer/utils/businessRules.js'
);

import { debounce, isEqual, omitBy } from 'lodash-es';

// import DynamicDialog from '@/form-designer/components/form-render/dynamic-dialog.vue';
const  DynamicDialog  = formDesigner.getExportResource(
  'src/form-designer/components/form-render/dynamic-dialog.vue'
);

// import { PROCESS_LOG_NEED_COMPARE_WIDGETS } from '@/form-designer/extension/config.js';
const  { PROCESS_LOG_NEED_COMPARE_WIDGETS }  = formDesigner.getExportResource(
  'src/form-designer/extension/config.js'
);

// import photoUrl from "../../../basic/assets/image/card-no-data.svg";
// const {photoUrl} = basic.getExportResource('src/basic/assets/image/card-no-data.svg').default;

export default {
  name: 'VFormRenderRewrite',
  extends: VFormRender,
  inject: ['viewType'],
  props: {
    isArrayData: {
      //prop传入的表单数据
      type: Boolean,
      default: () => false
    },
    formViewType: {
      type: String,
      default: ''
    },
    isRefreshFileId:{
      type: Boolean,
      default: () => true
    }
  },
  provide() {
    return {
      onObjectSelectChange: this.onObjectSelectChange,
      getFormRenderRef: () => this,
      viewType: this.viewType || this.formViewType
    };
  },
  data() {
    return {
      rules: [],
      controlRulseObj: {}, // 记录表单当前被业务规则和流程权限控制的所有权限控制数据
      cascadExcuseFunc: {}, // 记录级联约束多规则执行函数
      cascadRulesObj: {}, // 所有规则关联的条件属性以及对应的构造数据
      watchArr: [], // 存储$watch的解绑函数
      cascadWatchArr: [], // 存储$watch的解绑函数
      refershFileWatch: [], // 刷新附件监听解绑函数
      allTypeAndClassRuleMap: {}, // 存储所有类型和分类的级联约束规则
      // 类型取值规则map
      typeRuleMap: {},
      // 分类取值规则map
      classRuleMap: {},
      // 类型_分类 取值规则map
      typeAndClassRuleMap: {},
      typeOtype: 'ty.inteplm.type.CTyTypeDef',
      classificationOtype: 'ty.inteplm.type.CTyClassification',
      loadCount: 0, // 加载次数
      // 查询次数
      fildFieldCount: 0,
      // 存储可能循环的属性的规则map
      cycleAttrRuleMap: new Map(),
      hasGetEnumMap: false // 是否已获取表单系统枚举选项
    };
  },
  computed: {
    selectEnumMap() {
      return Object.values(getAllFieldWidgets(this.widgetList)).reduce((pre, item) => {
        if (item?.field && ['radio', 'checkbox'].includes(item.field.type) && item.field.options.selectEnumOid) {
          // modify by nieyz 2025/4/9 兼容多个组件选同一个系统枚举场景
          if (!pre[item.field.options.selectEnumOid]) {
            pre[item.field.options.selectEnumOid] = [item.field];
          } else {
            pre[item.field.options.selectEnumOid].push(item.field)
          }
        }
        return pre;
      }, {});
    },
    // 存储所有类型和分类的级联约束规则
    allCascadRuleMap() {
      return this.getWhiteListRuleMap(this.typeAndClassRuleMap, ['cascading']);
    },
    // 存储所有类型和分类的级联约束+文本规则
    allCascadAndTextRuleMap() {
      return this.getWhiteListRuleMap(this.typeAndClassRuleMap, ['cascading', 'textCombination']);
    },
    // 当前类型_分类组合的key
    typeClassKey() {
      let viewType = this.viewType || this.formViewType;
      if(typeof viewType == 'function') viewType = viewType();
      if(!viewType || viewType == 'VIEW') return '';
      if(!this.formData?.classDefName?.length && !this.formData?.typeDefName?.length) return '';
      if(this.formData?.classDefName?.length && !this.formData?.typeDefName?.length) {
        return `${this.formData?.classDefName[0].oid || ''}`;
      };
      if(!this.formData?.classDefName?.length && this.formData?.typeDefName?.length) {
        return `${this.formData?.typeDefName[0].oid || ''}`;
      };
      return `${this.formData?.typeDefName[0].oid || ''}_${this.formData?.classDefName[0].oid || ''}`;
    },
    // 存储当前类型和分类的所有级联约束规则
    allCascadRuleList() {
      return this.allCascadRuleMap[this.typeClassKey] || [];
    }
  },
  watch: {
    typeClassKey: {
      async handler(val, oldVal) {
        if(!val || val == oldVal) return;
        // modify by wangyf 2025/05/20 需先等待表单数据加载完成
        this.onTypeClassKeyChange(val);
      },
      immediate: true
    },
    selectEnumMap: {
      handler() {
        // 获取当前表单实例所有附件，版本变化时附件的fileId会变化，需要更新
        if (!this.hasGetEnumMap) this.getAllEnumOptions();
      },
      immediate: true
    },
    'formJsonObj.formConfig.businessRules': {
      handler: function (businessRules) {
        this.controlRulseObj = {};
        // 解绑上一次的 watch
        this.watchArr.forEach((func) => typeof func === 'function' && func());
        this.watchArr = [];
        if (!businessRules || !businessRules.length || businessRules.every((item) => item.enable == 0)) return;
        const fields = getAllFieldWidgets(this.formJsonObj.widgetList, true);
        const containers = getAllContainerWidgets(this.formJsonObj.widgetList, ['tab-pane']);
        const { allFields } = this.getAllWidgetsClone();
        // 将所有组件数据转换成options数据
        const allFieldOptions = {
          ...containers.reduce((pre, { name, container: { options } }) => {
            pre[name] = options;
            return pre;
          }, {}),
          ...fields.reduce((pre, { name, field: { options } }) => {
            pre[name] = options;
            return pre;
          }, {})
        };
        this.controlRulseObj = mergeActions({}, allFieldOptions, true);
        const rulesObj = getConditionMap(businessRules);
        Object.keys(rulesObj).forEach((name) => {
          // 记录所有watch的解绑函数
          this.watchArr.push(
            this.$watch(
              `formDataModel.${name}`,
              debounce(() => {
                // 是否需要刷新模版标识
                let isNeedRefresh = false;
                rulesObj[name].forEach(({ judgeCondition, actions, applyedAction }, index) => {
                  const isConditionTrue = judgeCondition(this.formDataModel);
                  // 条件未生效过，且当前满足该条件，执行该条件对应的动作
                  if (!applyedAction && isConditionTrue) {
                    const action = actions(allFields);
                    // 记录动作，用于判断是否执行过改动作与还原该动作
                    rulesObj[name][index].applyedAction = action;
                    mergeActions(this.controlRulseObj, action);
                    isNeedRefresh = true;
                  } else if (applyedAction && !isConditionTrue) {
                    // 条件已经生效，且当前不满足该条件时，需要还原该条件对应的动作
                    resetActions(this.controlRulseObj, applyedAction);
                    // 清空动作记录，用于后续执行该动作
                    rulesObj[name][index].applyedAction = null;
                    isNeedRefresh = true;
                  }
                });
                if (isNeedRefresh) {
                  isNeedRefresh = false;
                  // 合并流程权限对模版的控制与业务规的控制
                  mergeActions(this.controlRulseObj, this.formJsonObj.formConfig.grantedParams || {});
                  // modify by nieyz 2024/12/04 拷贝一份值是因为直接修改widgetList里组件的引用会有值不更新的问题，这里是批量修改完组件属性后再统一给widgetList赋值
                  const { allFields: allWidgets, widgetList } = this.getAllWidgetsClone();
                  Object.keys(this.controlRulseObj).forEach((name) => {
                    const widget = allWidgets.find((item) => item.name === name);
                    if (!widget) return;
                    // 基础组件跟容器组件取值不一样
                    const field = widget.field || widget.container;
                    // 根据优先级规则解析每个属性的控制值
                    const data = applyAttribute(this.controlRulseObj[name]);
                    // modify by nieyz 2025/4/9 设置组件校验属性后初始化组件的校验规则， 隐藏的组件不构建其校验规则，等它显示再构建
                    if (data.hasOwnProperty('required') && !data.hidden && this.widgetRefList[name]) {
                      const widgetRef = this.widgetRefList[name];
                      // modify by nieyz 2025/4/9 组件构造校验规则时必须为非隐藏状态
                      widgetRef.field.options.hidden = false;
                      widgetRef.setRequired(!!data.required);
                    }
                    Object.assign(field.options, data);
                  });
                  // 批量修改完组件属性后再统一给widgetList赋值
                  this.formJsonObj.widgetList = widgetList;
                }
              }, 500),
              { immediate: true, deep: true }
            )
          );
        });
      },
      deep: true,
      immediate: true
    }
  },
  methods: {
    /**
     * @description 更新是否已获取系统枚举选项标识
     * @author nieyz
     * @Date 2025-9-2 17:37:43
     * @param val 是否已获取标识 boolean
     */
    setEnumMapFlag(val) {
      this.hasGetEnumMap = val;
    },
    /**
     * @description 监听key变化执行
     * @Author wangyf
     * @param {*} val
     * @Date 2025-05-20 15:49:28
     */
    async onTypeClassKeyChange(val) {
        if(!val) return;
        // 解绑之前的的 watcher
        this.cascadWatchArr.forEach(item => typeof item.func === 'function' && item.func());
        this.cascadWatchArr = [];
        let arr = val.split('_');
        arr[0] && await this.queryRuleListByRefTypeList([arr[0]], this.typeOtype, val);
        arr[1] && await this.queryRuleListByRefTypeList([arr[1]], this.classificationOtype, val);
        // 获取页面双向绑定的allFields
        const allFields = getAllFieldWidgets(this.formJsonObj.widgetList, true);
        if(!allFields?.length && this.fildFieldCount < 10) {
          this.fildFieldCount++;
          setTimeout(() => {
            this.onTypeClassKeyChange(val);
          }, 500);
          return;
        };
        this.toSetCascadeWatcher(this.allCascadRuleList, allFields);
    },
    toSetCascadeWatcher: debounce(function(val, allFields) {
      // modify by wyf 2025-06-12 15:01:02 切换类型、分类先重置
        this.resetCascadeData(allFields);
        if (!val || !val.length || val.every((item) => item.enabled == 0)) return;
        // 判断是否存在枚举组件不存在枚举选项的情况
        const existNotEnum = allFields.some(item => item.type == 'radio' && !item.field.options?.optionItems?.length);
        if(!allFields?.length) return;
        // 存在的话则每500ms重新执行一次，最多十次
        if(existNotEnum && this.loadCount < 10) {
          this.loadCount++;
          setTimeout(() => {
            this.toSetCascadeWatcher(val, allFields);
          }, 500);
          return;
        }
        this.$nextTick(() => {
          // 需要校验死循环的规则列表
          let needCheckRuleList = [];
          // 对级联约束进行排序
          let sortAllCascadRuleList = val.sort((a, b) => a.ruleIndex - b.ruleIndex);
          // 遍历各个规则的级联约束
          sortAllCascadRuleList.forEach(rule => {
            const ruleValueObj = JSON.parse(rule.ruleValue);
            // modify by wyf 2025-05-14 15:01:02 筛选掉布局中不存在的属性
            const filterRuleValueObj = handleFilterRuleValueObj(ruleValueObj, allFields);
            // 将过滤后的规则添加到校验规则列表中
            needCheckRuleList.push({...filterRuleValueObj,ruleName: rule.name, ruleOid: rule.oid});
            // 如果条件为空或者级联约束为空，则跳过
            if(!filterRuleValueObj?.actionList?.length || !filterRuleValueObj?.conditionList?.length) return;
            // 构造数据结构
            const rulesObj = getcascadConditionMap(filterRuleValueObj);
            Object.keys(rulesObj).forEach(name => {
              // 对不同规则同属性进行融合，监听数据变化时 都需要判断其条件是否满足，及执行级联约束
              if (!this.cascadRulesObj[name]) {
                this.cascadRulesObj[name] = [];
              }
              this.cascadRulesObj[name].push(...rulesObj[name]);
            });
          })
          // 校验前先清空
          this.cycleAttrRuleMap = new Map();
          // 校验规则是否有死循环
          this.checkAllRuleList(needCheckRuleList);
          
          // 遍历数据结构对象添加watcher
          Object.keys(this.cascadRulesObj).forEach(name => {
            // 记录所有属性的watch的解绑函数
            this.cascadWatchArr.push({
              name,
              func: this.$watch(
                `formDataModel.${name}`,
                debounce(() => {
                  let cloneFormDataModel = deepClone(this.formDataModel);
                  if(!this.cascadWatchArr?.length) return;
                  // modify by wyf 2025-05-14 15:01:02 处理异常状态
                  if (!this.cascadRulesObj[name]?.length) return;
                  // 如果当前变化的属性是死循环中的一个 需校验 当前执行是否会导致死循环触发
                  if (this.cycleAttrRuleMap.get(name)?.length > 0) {
                    let list = this.cycleAttrRuleMap.get(name);
                    // 找到触发死循环的那条链路
                    let useCycleObj = {}
                    let flag = list.some(obj => {
                      let result = this.checkRuleUseCycle(cloneFormDataModel, obj.cycleRuleList);
                      if (result) {
                        useCycleObj = {...obj};
                      }
                      return result
                    })
                    if (flag) {
                      let attrstr = useCycleObj.cycleAttrNameList.join(',');
                      let rulestr = useCycleObj.cycleRuleNameList.join(',');
                      return this.$tyPlmMessage({
                        type: 'info',
                        // modify by caoy 2025/05/23 改为手动关闭
                        duration: 0,
                        showClose: true,
                        message: this.$t('basic.cycleInfoTip', {
                          attr: attrstr,
                          rule: rulestr
                        })
                      });
                    }
                  }
                  // 要进行重置的数据
                  let resetFormData = {};
                  // 要修改的数据
                  let excuseFormData = {};
                  // 要进行重置的配置
                  let resetFormOptions = {};
                  // 要修改的配置
                  let excuseFormOptions = {};
                  this.cascadRulesObj[name].forEach(({ judgeCondition, excuseCascades, lastCascade }, index) => {
                    // 判断条件是否成立
                    const isConditionTrue = judgeCondition(this.formDataModel);
                    // 条件未生效过，且当前满足该条件，执行该条件对应的动作
                    if (!lastCascade && isConditionTrue) {
                      const {lastCascade: lastCascadeData, newActionFormData, newActionFormOption} = excuseCascades(cloneFormDataModel, allFields);
                      // 记录上一次的值以及属性配置
                      this.cascadRulesObj[name][index].lastCascade = lastCascadeData;
                      excuseFormData = {...excuseFormData, ...newActionFormData};
                      excuseFormOptions = { ...excuseFormOptions, ...newActionFormOption};
                    } else if (lastCascade && !isConditionTrue) {
                      // 条件已经生效，且当前不满足该条件时，需要还原该条件对应的之前值和属性配置
                      const { resetFormOption } = resetCascadeActions(allFields, lastCascade);
                      resetFormData = {...resetFormData, ...lastCascade};
                      resetFormOptions = { ...resetFormOptions, ...resetFormOption};
                      // 清空动作记录，用于后续执行该动作
                      this.cascadRulesObj[name][index].lastCascade = null;
                    }
                  });
                  cloneFormDataModel = {...cloneFormDataModel, ...resetFormData, ...excuseFormData};
                  const combineFormOption = {
                    ...resetFormOptions,
                    ...excuseFormOptions
                  }
                  // 找出 cloneFormDataModel 中不同于 formDataModel 的属性
                  const modelDiff = omitBy(cloneFormDataModel, (value, key) => isEqual(value, this.formDataModel[key])) || {};
                  // modify by wyf 2025-06-12 15:01:02 批量更新
                  this.setFormData(modelDiff);
                  this.setFormOptions(combineFormOption, allFields)
                  cloneFormDataModel = null;
                  this.filterTypeAndClass(name);
                }, 500),
                { immediate: true, deep: true }
              )
            });
          });
        })
    }, 500),
    /**
     * @description 批量处理属性配置，防止符合条件的配置被重置配置覆盖
     * @Author wangyf
     * @param {*} combineFormOption
     * @param {*} allFields
     * @Date 2025-06-23 15:49:28
     */
    setFormOptions(combineFormOption, allFields) {
      Object.keys(combineFormOption).forEach(name => {
        const findField = allFields.find(field => field.name === name);
        if(findField?.type == 'radio') {
          // 处理配置
          findField.field.options.optionItems = combineFormOption[name] || findField.field?.options?.allOptionItems ;
        }
      })
    },
    /**
     * @description 分类/类型切换先重置
     * @Author wangyf
     * @param {*} allFields
     * @Date 2025-06-12 15:49:28
     */
    resetCascadeData(allFields) {
        let beforeResetData = {};
        // 先重置监听改变的级联约束，不然切换类型或者分类的时候，改变的值都
        if(Object.keys(this.cascadRulesObj)?.length > 0){
          Object.keys(this.cascadRulesObj).forEach(name => {
            this.cascadRulesObj[name].forEach(({ lastCascade }, index) => {
              if (lastCascade ) {
                // 条件已经生效，且当前不满足该条件时，需要还原该条件对应的之前值和属性配置
                resetCascadeActions(allFields, lastCascade);
                // 直接切换分类/类型时 记录要还原的值
                beforeResetData = {...beforeResetData, ...lastCascade};
                // 清空动作记录，用于后续执行该动作
                this.cascadRulesObj[name][index].lastCascade = null;
              }
            })
          })
        }
        this.setFormData(beforeResetData);
        // 清空所有规则关联的条件属性以及对应的构造数据
        this.cascadRulesObj = {};
    },
    /**
     * @description 分类/类型的watcher 第一次执行后给解绑，后续获取级联时重新绑定
     * @Author wangyf
     * @param {*} oidList
     * @param {*} otype
     * @Date 2025-05-16 15:49:28
     */
    filterTypeAndClass(name) {
      if(name == 'typeDefName' || name == 'classDefName') {
        const findWatcher = this.cascadWatchArr.find(item => item.name === name);
        if(findWatcher) {
          findWatcher.func();
        }
        this.cascadWatchArr = this.cascadWatchArr.filter(item => item.name !== name);
      }
    },
    /**
     * @description 批量获取 类型/分类 取值规则
     * @Author wangyf
     * @param {*} oidList
     * @param {*} otype
     * @Date 2025-05-07 15:49:28
     */
    async queryRuleListByRefTypeList (oidList, otype, typeClassKey) {
      let param = {
        oidList,
        otype
      };
      const res = await request({
        method: 'post',
        url: '/rest/v1/TypeValueRule/queryRuleListByRefTypeList',
        data: param
      });
      if (res?.data?.success && res?.data?.data) {
        let resData = res?.data?.data || {};
        const curClassOid = this.formData?.classDefName?.length && this.formData?.classDefName[0]?.oid;
        const curTypeOid = this.formData?.typeDefName?.length && this.formData?.typeDefName[0]?.oid;
        Object.keys(resData).forEach(key => {
          if (otype == this.typeOtype) {
            // 类型取值规则map
            this.typeRuleMap[key] = resData[key];
            this.typeAndClassRuleMap[typeClassKey] = [...resData[key]];
            // 类型_分类取值规则合集map
            if(curClassOid) {
              this.typeAndClassRuleMap[typeClassKey] = [...this.typeAndClassRuleMap[typeClassKey], ...(this.classRuleMap[curClassOid] || [])];
            }
          } else {
            // 分类取值规则map
            this.classRuleMap[key] = resData[key];
            this.typeAndClassRuleMap[typeClassKey] = [...resData[key]];
            // 类型_分类取值规则合集map
            if(curTypeOid) {
              this.typeAndClassRuleMap[typeClassKey] = [...this.typeAndClassRuleMap[typeClassKey], ...(this.typeRuleMap[curTypeOid] || [])];
            }
          }
        });
      }
    },
    /**
     * @description 获取白名单取值规则
     * @Author wangyf
     * @param {*} typeAndClassRuleMap
     * @param {*} whiteList
     * @Date 2025-05-08 15:49:28
    */
    getWhiteListRuleMap(typeAndClassRuleMap, whiteList) {
      if(!typeAndClassRuleMap) return {};
      let newMap = {};
      Object.keys(typeAndClassRuleMap).forEach(key => {
        newMap[key] = typeAndClassRuleMap[key].filter(item => whiteList.includes(item.ruleType));
        if(!newMap[key].length) {
          delete newMap[key];
        }
      })
      return newMap;
    },
    getAllWidgetsClone() {
      const widgetList = deepClone(this.formJsonObj.widgetList);
      const fields = getAllFieldWidgets(widgetList, true);
      const containers = getAllContainerWidgets(widgetList, ['tab-pane']);
      return {
        allFields: containers.concat(fields),
        widgetList
      };
    },
    addFieldChangeEventHandler() {
      this.off$('fieldChange'); //移除原有事件监听
      // modify by weig 2024-09-12 09:01:02 接收fieldChange多接收一个参数field
      this.on$('fieldChange', ([fieldName, newValue, oldValue, subFormName, subFormRowIndex, field]) => {
        this.handleFieldDataChange(fieldName, newValue, oldValue, subFormName, subFormRowIndex, field);
        this.$emit('formChange', fieldName, newValue, oldValue, this.formDataModel, subFormName, subFormRowIndex, field);
      });
    },
    setFormJson(newFormJson) {
      if (!!newFormJson) {
        if (typeof newFormJson === 'string' || newFormJson.constructor === Object) {
          let newFormJsonObj = null;
          if (typeof newFormJson === 'string') {
            newFormJsonObj = JSON.parse(newFormJson);
          } else {
            newFormJsonObj = newFormJson;
          }

          if (!newFormJsonObj.formConfig || !newFormJsonObj.widgetList) {
            // modify by nieyz 2024/4/11 去掉json数据类型错误提示，改为缺省图显示
            return;
          }
          /* formDataModel必须在widgetList赋值完成初始化，因为widgetList赋值意味着子组件开始创建！！！ */
          //this.formDataModel = {}  //清空表单数据对象（有bug，会导致表单校验失败！！）
          this.clearFormDataModel(); //上行代码有问题，会导致表单校验失败，故保留原对象引用只清空对象属性！！
          this.buildFormModel(newFormJsonObj.widgetList);
          // modify by nieyz 2025/9/2 重置枚举获取标识
          this.setEnumMapFlag(false);
          this.formJsonObj['formConfig'] = newFormJsonObj.formConfig;
          this.formJsonObj['widgetList'] = newFormJsonObj.widgetList;

          this.insertCustomStyleAndScriptNode(); /* 必须先插入表单全局函数，否则VForm内部引用全局函数会报错！！！ */
          this.$nextTick(() => {
            this.initFormObject(false);
            this.initDataSetRequest();
            this.broadcast('FieldWidget', 'reloadOptionItemsByType');
            this.handleOnMounted();
          });
          // modify by nieyz 2024/4/11 去掉json数据类型错误提示，改为缺省图显示
        }
      }
    },
    setFormData(formData) {
      //设置表单数据
      let formDataObj = formData;
      if (typeof formData === 'string') {
        formDataObj = JSON.parse(formData);
      }
      Object.keys(this.formDataModel).forEach((propName) => {
        if (!!formDataObj && formDataObj.hasOwnProperty(propName)) {
          this.formDataModel[propName] = deepClone(formDataObj[propName]);
        }
      });
      // 通知SubForm组件：表单数据更新事件！！
      this.broadcast('ContainerItem', 'setFormData', this.formDataModel);
      // 通知FieldWidget组件：表单数据更新事件！！
      this.broadcast('FieldWidget', 'setFormData', this.formDataModel);
    },
    initFieldsDisplayText() {
      // 通知FieldWidget组件：组件现实值更新事件！！
      this.broadcast('ContainerItem', 'initFieldsDisplayText');
      this.broadcast('FieldWidget', 'initFieldsDisplayText');
    },
    initFormData(formData) {
      //设置表单数据
      let formDataObj = formData;
      if (typeof formData === 'string') {
        formDataObj = JSON.parse(formData);
      }
      Object.keys(this.formDataModel).forEach((propName) => {
        if (!!formDataObj && formDataObj.hasOwnProperty(propName)) {
          this.formDataModel[propName] = deepClone(formDataObj[propName]);
        }
      });
      // 通知SubForm组件：表单数据更新事件！！
      this.broadcast('ContainerItem', 'initFormData', this.formDataModel);
      // 通知FieldWidget组件：表单数据更新事件！！
      this.broadcast('FieldWidget', 'initFormData', this.formDataModel);
    },
    // 清空附件上传组件的addFileList和removeFileList
    resetFileUploadAddRemoveList() {
      this.broadcast('FieldWidget', 'resetFileUploadAddRemoveList');
    },
    // 清空关联记录组件的addList和removeList
    resetRelateApplicationAddRemoveList() {
      this.broadcast('FieldWidget', 'resetRelateApplicationAddRemoveList');
    },
    /**
     * @description 表单图片组件自动上传的时候文件没上传完的校验逻辑
     * @author weig
     * @date 2024-09-24 17:54:02
     * @returns {Boolean} 上传完成返回true/没完成返回false
     */
    validatePictureList() {
      let flag = true;
      const pictureUploadRefLists = Object.values(this.widgetRefList || []).filter((item) => item.field?.type === 'picture-upload');
      flag = pictureUploadRefLists.every((val) => val.uploadCompleted);
      return flag;
    },
    getFormData(needValidation = true, needFileList = false) {
      let formData = needFileList ? { ...this.formDataModel, ...this.getUploadFile() } : this.formDataModel;
      if (!needValidation) {
        return formData;
      }
      // modify by weig 2024-09-24 Fixed bug23754 【SIT-MYSQL】【冒烟】新建表单报未知异常
      // 校验表单中的图片组件中的所有的图片上传是否完成的逻辑
      const flag = this.validatePictureList();
      if (!flag) {
        return this.$message.warning(this.i18nt('designer.setting.uploading'));
      }

      let callback = function nullFunc() {};
      let promise = new window.Promise(function (resolve, reject) {
        callback = function (formData, error) {
          !error ? resolve(formData) : reject(error);
        };
      });

      this.$refs['renderForm'].validate((valid) => {
        if (valid) {
          // 校验自定义组件的方法
          this.getCustomComponentData()
            .then((customComponentData) => {
              formData = { ...formData, ...customComponentData };
              callback(formData);
            })
            .catch((err) => {
              callback(formData, this.i18nt('render.hint.validationFailed'));
            });
        } else {
          callback(formData, this.i18nt('render.hint.validationFailed'));
        }
      });

      return promise;
    },
    getUploadFile() {
      const [list, removeList, addList] = [[], [], []];
      const fileUploadRefLists = Object.values(this.widgetRefList).filter((item) => item.field?.type === 'file-upload');
      fileUploadRefLists.forEach((comp) => {
        const { fileList, removeFileList, addFileList } = comp;
        list.push({
          roleKey: comp.field.options.name,
          files: fileList
        });
        // modify by nieyz 2024/6/17 修复removeFileList为空数组报错问题
        removeFileList?.length && removeList.push(...removeFileList);
        addFileList?.length && addList.push(...addFileList);
      });
      return { fileList: list, removeFileList: removeList, addFileList: addList };
    },
    // 获取自定义组件数据
    getCustomComponentData() {
      let customComponentDatas = {};
      const customRefLists = Object.values(this.widgetRefList).filter((item) => item.field?.type === 'custom-component');
      return new Promise((resolve, reject) => {
        Promise.all(customRefLists.map((comp) => comp.getData()))
          .then((res) => {
            customRefLists.forEach((comp, index) => {
              customComponentDatas[comp.fieldKeyName] = res[index];
            });
            resolve(customComponentDatas);
          })
          .catch((err) => {
            reject(err);
          });
      });
    },
    toFormData(formData, widgetType, key) {
      if (!formData[key] || !formData[key].length || !widgetType) {
        return '';
      }
      if (widgetType === 'select-user') {
        return formData[key][0]?.fullname
          ? formData[key].map((item) => ({
            // modify by nieyz 2025/6/9 适配新人员组件显示，不带（）
            rowLabel: item.rowLabel || item.fullname,
            rowValue: item.rowValue || item.oid,
            // modify by nieyz 2025/4/10 适配新人员组件的详情弹窗，增加name传参
            name: item.name || (item.rowLabel && /\(.*?\)/.test(item.rowLabel) ? item.rowLabel.match(/.*\((.*)\)/)[1] : item.rowLabel),
            oid: item.oid,
            otype: item.otype
          }))
          : null;
      }
      if (
        [
          'number',
          'unit',
          'radio',
          'time',
          'date',
          'textarea',
          'switch',
          'area',
          'rich-editor',
          'number-generation',
          'select-object',
          'select-compose',
          'formula-calc'
        ].includes(widgetType)
      ) {
        return formData[key][0]?.value || formData[key][0]?.text || '';
      }
      if (['input'].includes(widgetType)) {
        return formData[key][0]?.text || formData[key][0]?.value || '';
      }
      if (['checkbox', 'time-range', 'date-range'].includes(widgetType)) {
        return formData[key].map((item) => item.value);
      }
      return Array.isArray(formData[key]) ? formData[key][0] : formData[key] || '';
    },
    buildDataFromWidget(wItem) {
      if (wItem.category === 'container') {
        if (wItem.type === 'vf-dialog' || wItem.type === 'vf-drawer') {
          // 什么也不做，不处理弹窗、抽屉内部组件！！
        } else if (wItem.type === 'grid') {
          if (!!wItem.cols && wItem.cols.length > 0) {
            wItem.cols.forEach((childItem) => {
              this.buildDataFromWidget(childItem);
            });
          }
        } else if (wItem.type === 'table') {
          if (!!wItem.rows && wItem.rows.length > 0) {
            wItem.rows.forEach((rowItem) => {
              if (!!rowItem.cols && rowItem.cols.length > 0) {
                rowItem.cols.forEach((colItem) => {
                  this.buildDataFromWidget(colItem);
                });
              }
            });
          }
        } else if (wItem.type === 'tab') {
          if (!!wItem.tabs && wItem.tabs.length > 0) {
            wItem.tabs.forEach((tabItem) => {
              if (!!tabItem.widgetList && tabItem.widgetList.length > 0) {
                tabItem.widgetList.forEach((childItem) => {
                  this.buildDataFromWidget(childItem);
                });
              }
            });
          }
        } else if (wItem.type === 'data-table') {
          let fKeyName = this.getFieldKeyName(wItem);
          if (this.formData.hasOwnProperty(fKeyName)) {
            let initialValue = this.formData[fKeyName];
            this.formDataModel[fKeyName] = deepClone(initialValue);
          } else if (Array.isArray(wItem.options.tableData)) {
            // modify by nieyz 2025/5/21 修复数据表格数据回显问题
            this.formDataModel[fKeyName] = deepClone(wItem.options.tableData);
          } else {
            // modify by nieyz 2025/2/25 修复数据表格填写数据保存后不能正常回显问题
            this.formDataModel[fKeyName] = []
          }
        } else if (wItem.type === 'sub-form') {
          let subFormName = wItem.options.name;
          if (!this.formData.hasOwnProperty(subFormName)) {
            if (Array.isArray(wItem.options.defaultValue) && wItem.options.defaultValue.length) {
              // 子表单有默认值时取自己的默认值
              this.formDataModel[subFormName] = deepClone(wItem.options.defaultValue);
            } else if (wItem.options.defaultRows) {
              // 当子表单设置了空行数时
              const subFormDataRow = {};
              wItem.widgetList.forEach((subFormItem) => {
                if (!!subFormItem.formItemFlag) {
                  let sfiKeyName = this.getFieldKeyName(subFormItem);
                  subFormDataRow[sfiKeyName] = subFormItem.options.defaultValue;
                }
              });
              const subformData = [];
              let i = 0;
              while (i < wItem.options.defaultRows) {
                subformData.push(deepClone(subFormDataRow));
                i += 1;
              }
              this.formDataModel[subFormName] = subformData;
            } else {
              this.formDataModel[subFormName] = [];
            }
          } else {
            let initialValue = this.formData[subFormName];
            this.formDataModel[subFormName] = deepClone(initialValue);
          }
        } else if (wItem.type === 'grid-sub-form') {
          let gridSubFormName = wItem.options.name;
          if (!this.formData.hasOwnProperty(gridSubFormName)) {
            let gsfFWList = [];
            let fieldListFn = (fw) => {
              gsfFWList.push(fw);
            };
            traverseFieldWidgetsOfContainer(wItem, fieldListFn);

            let gridSubFormDataRow = {};
            if (wItem.options.showBlankRow) {
              gsfFWList.forEach((gridSubFormItem) => {
                let sfiKeyName = this.getFieldKeyName(gridSubFormItem);
                gridSubFormDataRow[sfiKeyName] = gridSubFormItem.options.defaultValue;
              });
              this.formDataModel[gridSubFormName] = [gridSubFormDataRow];
            } else {
              this.formDataModel[gridSubFormName] = [];
            }
          } else {
            let initialValue = this.formData[gridSubFormName];
            this.formDataModel[gridSubFormName] = deepClone(initialValue);
          }
        } else if (wItem.type === 'grid-col' || wItem.type === 'table-cell') {
          if (!!wItem.widgetList && wItem.widgetList.length > 0) {
            wItem.widgetList.forEach((childItem) => {
              this.buildDataFromWidget(childItem);
            });
          }
        } else if (wItem.type === 'object-group') {
          // 处理对象容器内部字段！！
          let objectFields = [];
          traverseFieldWidgetsOfContainer(wItem, (fld) => {
            if (!!fld.formItemFlag) {
              objectFields.push(fld);
            }
          });

          let objectName = wItem.options.objectName;
          objectFields.forEach(({ type, options: { name: fieldName, isCurrentValue, defaultValue, format, tabList } }) => {
            let objPath = objectName + '.' + fieldName;
            const viewType = this.viewType || this.formViewType;
            if (!hasPropertyOfObject(this.formData, objPath)) {
              let defaultVal = defaultValue;
              // 处理日期组件选择当前值的场景
              if (viewType === 'CREATE' && isCurrentValue && type === 'date') {
                defaultVal = formatDate(new Date(), format);
              } else if (viewType === 'CREATE' && isCurrentValue && type === 'select-user' && tabList[0] === 'user') {
                // 处理人员组件选择当前值的场景
                defaultVal = [getCurUser()];
              }
              setObjectValue(this.formDataModel, objPath, defaultVal);
            } else {
              let initialValue = getObjectValue(this.formData, objPath);
              setObjectValue(this.formDataModel, objPath, initialValue);
            }
          });
        } else {
          //自定义容器组件
          if (!!wItem.widgetList && wItem.widgetList.length > 0) {
            wItem.widgetList.forEach((childItem) => {
              this.buildDataFromWidget(childItem);
            });
          }
        }
      } else if (!!wItem.formItemFlag) {
        let fKeyName = this.getFieldKeyName(wItem);
        if (!this.formData.hasOwnProperty(fKeyName)) {
          const {
            type,
            options: { isCurrentValue, defaultValue, format, tabList }
          } = wItem;
          const viewType = this.viewType || this.formViewType;
          // 处理日期组件选择当前值的场景
          if (viewType === 'CREATE' && isCurrentValue && type === 'date') {
            this.formDataModel[fKeyName] = formatDate(new Date(), format);
          } else if (viewType === 'CREATE' && isCurrentValue && type === 'select-user' && tabList[0] === 'user') {
            // 处理人员组件选择当前值的场景
            this.formDataModel[fKeyName] = [getCurUser()];
          } else {
            this.formDataModel[fKeyName] = defaultValue;
          }
        } else {
          const initData = deepClone(this.formData);
          let initialValue = this.isArrayData ? this.toFormData(initData, wItem.type, fKeyName) : initData[fKeyName];
          this.formDataModel[fKeyName] = deepClone(initialValue);
        }
      }
    },
    /**
     * @description 通过对象修改表单模板数据
     * @param {*} obj
     * @author caoy
     * @Date 2025-05-06 09:22:18
     */
     changeFormDataByObj(obj) {
      obj && this.setFormData({ ...this.formDataModel, ...obj });
    },
    onObjectSelectChange(selectObject) {
      selectObject && this.setFormData({ ...this.formDataModel, ...selectObject });
    },
    /**
     * 显示弹窗表单，动态创建v-form-render组件，option-data、global-dsv等属性继承父级表单
     * @param dialogName
     * @param formData
     * @param extraData
     * @param title
     */
    showDialog(dialogName, formData = {}, extraData = {}, title = '') {
      let topFormRef = this.getTopFormRef();
      let dialogCon = getContainerWidgetByName(topFormRef.widgetList, dialogName);
      if (!dialogName || dialogCon.type !== 'vf-dialog') {
        this.$message.error(this.i18nt('render.hint.refNotFound') + dialogName);
        return;
      }
      let dFormJson = {
        widgetList: deepClone(dialogCon.widgetList),
        formConfig: cloneFormConfigWithoutEventHandler(topFormRef.formConfig)
      };

      let wrapperDivId = generateId() + '';
      let dialogInstance = createVNode(
        DynamicDialog,
        {
          options: dialogCon.options,
          formJson: dFormJson,
          formData: formData || {},
          optionData: topFormRef.optionData,
          globalDsv: topFormRef.globalDsv,
          parentFormRef: this,
          extraData: extraData,
          wrapperId: wrapperDivId,
          title: title
        },
        this.$slots
      );
      dialogInstance.appContext = this.$root.$.appContext; //非常重要， 覆盖应用上下文！！

      let wrapperDiv = document.createElement('div');
      wrapperDiv.id = 'vf-dynamic-dialog-wrapper' + wrapperDivId;
      document.body.appendChild(wrapperDiv);
      render(dialogInstance, wrapperDiv);
      document.body.appendChild(dialogInstance.el);
      dialogInstance.component.ctx.show();
    },
    // 获取当前表单实例所有附件，版本变化时附件的fileId会变化，需要更新
    async refreshFileId() {
      if (!this.formData.curFormInstanceId || !this.formData.curFormInstanceOtype) return;
      const res = await request({
        method: 'post',
        url: '/rest/v1/fileHandle/getFileListByEntity',
        data: {
          oid: this.formData.curFormInstanceId,
          otype: this.formData.curFormInstanceOtype
        }
      });
      if (res?.data?.data) {
        // 通知表单所有附件组件：fileId有更新！！
        this.broadcast('FieldWidget', 'refreshFileId', res.data.data);
      }
    },
    // 获取所有绑定系统枚举的下拉组件的下拉选项并赋值
    async getAllEnumOptions() {
      const enumDefKeys = Object.keys(this.selectEnumMap);
      if (!enumDefKeys.length) return;
      const res = await request({
        method: 'post',
        url: '/rest/v1/enum/getEnumItemsMapWithColor',
        data: {
          enumDefKeys
        }
      });
      if (res?.data?.success) {
        const data = res.data.data;
        Object.keys(this.selectEnumMap).forEach((enumId) => {
          // 绑定当前枚举的所有组件
          const widgets = this.selectEnumMap[enumId];
          // modify by nieyz 2025/4/9 兼容多个组件选同一个系统枚举场景
          const optionItems = Object.values(data[enumId] || {}).map(({ colorchart, code, name }) => ({ label: name, value: code, color: colorchart }));
          widgets.forEach(widget => (widget.options.optionItems = optionItems));
        });
      }
      // 枚举请求完毕
      this.setEnumMapFlag(true);
    },
    getChangedFieldsDiff(needCompareWidgetsArr) {
      const compareWidgetsArr = needCompareWidgetsArr || PROCESS_LOG_NEED_COMPARE_WIDGETS;
      const diffArr = [];
      const handler = ({ type: fieldtype, options: { name: fieldname } }) => {
        const widgetRef = this.widgetRefList[fieldname];
        if (!widgetRef || !compareWidgetsArr.includes(fieldtype)) return;
        // 子表单差异比较
        if (['sub-form', 'grid-sub-form'].includes(fieldtype)) {
          const {
            deleteRows,
            initData,
            initRowIdArr,
            rowIdData,
            fieldWidgetList,
            widget: { widgetList }
          } = widgetRef;
          const subFormData = this.formDataModel[fieldname] || [];
          // 值未变不生产差异
          if (isEqual(initData, subFormData)) return;
          let [subFormArr, addNum, editNum, deleteNum] = [[], 0, 0, 0];
          const initIds = deepClone(initRowIdArr);
          subFormData.forEach((row, index) => {
            const rowObj = {};
            const subFormRowId = rowIdData[index];
            (fieldWidgetList || widgetList).forEach(({ type: widgetType, options: { name } }) => {
              const fieldRef = this.widgetRefList[name + '@row' + subFormRowId];
              if (!fieldRef || !compareWidgetsArr.includes(widgetType)) return;
              let { oldvalue, newvalue } = fieldRef?.getInitAndCurDisplay(false);
              if (['picture-upload', 'file-upload'].includes(widgetType)) {
                oldvalue = (oldvalue || '')
                  .replace(/=[^+]*(?=\+)/g, '')
                  .replaceAll('+', '，')
                  .replace(/，$/, '');
                newvalue = (newvalue || '')
                  .replace(/=[^+]*(?=\+)/g, '')
                  .replaceAll('+', '，')
                  .replace(/，$/, '');
              }
              rowObj[name] = { oldvalue, newvalue };
            });
            if (!initRowIdArr.includes(row._rowId_)) {
              addNum += 1;
              rowObj._isAdd_ = true;
            } else {
              // modify by nieyz 2025/3/13 当前行在初始行，不是删除行，去除
              initIds.splice(initIds.indexOf(row._rowId_), 1);
              const preRow = initData.find((item) => item._rowId_ === subFormData[index]._rowId_);
              if (preRow && !isEqual( subFormData[index], preRow)) {
                editNum += 1;
              }
            } 
            subFormArr.push(rowObj);
          });
          // 初始行不在当前行数据即为被删除数据
          deleteNum = initIds.length;
          deleteRows.forEach(({ _rowIndex_, ...rest }) => initIds.includes(rest._rowId_) && subFormArr.splice(_rowIndex_, 0, rest));
          diffArr.push({ fieldname, fieldtype, edittype: 1, newvalue: JSON.stringify({ diffData: subFormArr, addNum, editNum, deleteNum }), oldvalue: null });
        } else if (widgetRef?.getInitAndCurDisplay()) {
          let { oldvalue, newvalue } = widgetRef?.getInitAndCurDisplay();
          // 出掉图片与附件的id，得到纯文件名称用'，'拼接的字符串
          if (['picture-upload', 'file-upload'].includes(fieldtype)) {
            oldvalue = oldvalue
              .replace(/=[^+]*(?=\+)/g, '')
              .replaceAll('+', '，')
              .replace(/，$/, '');
            newvalue = newvalue
              .replace(/=[^+]*(?=\+)/g, '')
              .replaceAll('+', '，')
              .replace(/，$/, '');
          }
          diffArr.push({ fieldname, fieldtype, edittype: 1, oldvalue, newvalue });
        }
      };
      traverseFieldWidgetsNew(this.widgetList, handler, null, false, [], ['sub-form', 'grid-sub-form']);
      return diffArr;
    },
    /**
     * @description 校验规则列表是否有死循环
     * @param {*} ruleList
     * @author caoy
     * @Date 2025-05-19 09:02:27
     */
    checkAllRuleList(ruleList) {
      let transferList = []
      ruleList.forEach((rule) => {
        // 无可用条件和执行动作的规则不执行校验
        if (rule.actionList?.length <= 0 || rule.conditionList?.length <= 0) {
          return;
        }
        let obj = { from: [], to: []}
        // 把规则中条件部分加到from中
        rule.conditionList.forEach((condition) => {
          condition.filterConditionList.forEach((filterCondition) => {
            // modify by caoy 2025/05/20 判断是否是iba
            let attr = filterCondition.attr.otype == 'ty.inteplm.attribute.CTyAttributeDefMap' ? `${filterCondition.attr.realName}_iba` : filterCondition.attr.realName
            obj.from.push(attr)
          })
        })
        // 把规则中动作部分加到to中 每个属性加上规则名称和规则id 方便追溯
        rule.actionList.forEach(action => {
          // modify by caoy 2025/05/20 判断是否是iba
          let attr = action.attr.otype == 'ty.inteplm.attribute.CTyAttributeDefMap' ? `${action.attr.realName}_iba` : action.attr.realName
          obj.to.push({key: attr, name: action.attr.displayName, ruleName: rule.ruleName, ruleOid: rule.ruleOid})
        })
        transferList.push(obj)
      })
      const graph = {};
      for (const rule of transferList) {
        const { from, to } = rule;
        for (const source of from) {
          if (!graph[source]) graph[source] = [];
          // 存储 { key, 规则 }
          graph[source].push(...to); 
        }
      }
      let cycleList = this.checkAttrCycle(graph);
      if(cycleList?.length > 0) {
        let map = new Map();
        // 遍历每一个死循环链路
        cycleList.forEach(cycle => {
          // 每一个死循环链路 需要收集死循环所有的规则 死循环规则名称合集 死循环属性名称合集
          let cycleRuleList = [];
          let cycleRuleNameList = []
          let cycleAttrNameList = []
          let oidMap = new Map();
          // 遍历规则，生成死循环规则名称合集 死循环属性名称合集
          cycle.cycleEdges?.forEach(item => {
            let obj = ruleList.find(rule => rule.ruleOid === item.ruleOid)
            if(obj && !oidMap.get(obj.ruleOid)) {
              oidMap.set(item.ruleOid, item.ruleName)
              cycleRuleList.push(obj);
            }
            cycleRuleNameList.push(item.ruleName)
            cycleAttrNameList.push(item.name)
          })
          // 属性名称去重
          cycleRuleNameList = [...new Set(cycleRuleNameList)]
          cycle.cycleNodes.forEach(node => {
            let list = map.get(node) || [];
            let obj = {
              cycleRuleList,
              cycleRuleNameList,
              cycleAttrNameList
            };
            list.push(obj);
            // 每个属性可能再多个死循环规则中
            map.set(node, list);
          })
        })
        console.log('cycleList', cycleList, map);
        this.cycleAttrRuleMap = map;
      }
    },
    /**
     * @description 校验属性是否存在循环引用
     * @param {*} graph
     * @author caoy
     * @Date 2025-05-19 11:19:57
     */    
    checkAttrCycle(graph) {
      // 存储所有环
      const cycles = [];        
      function dfs(node, path, edgePath, visited) {
        if (path.includes(node)) {
          const cycleStart = path.indexOf(node);
          const cycleNodes = path.slice(cycleStart);
          const cycleEdges = edgePath.slice(cycleStart);
          // 标准化环表示（按首个节点排序）
          const normalized = [...cycleNodes].sort((a, b) => a.localeCompare(b)).join('→');
          const isDuplicate = cycles.some(c => 
            c.cycleNodes.sort((a, b) => a.localeCompare(b)).join('→') === normalized
          );
          if (!isDuplicate) {
            cycles.push({ cycleNodes, cycleEdges });
          }
          return;
        }

        if (visited.has(node)) return;
        visited.add(node);
        path.push(node);
        // 去除当前属性作为条件时  可影响的执行动作
        const neighbors = graph[node] || [];
        for (const neighbor of neighbors) {
          edgePath.push({
            from: node,
            to: neighbor.key,
            name: neighbor.name,
            ruleName: neighbor.ruleName,
            ruleOid: neighbor.ruleOid,
          });
          dfs(neighbor.key, path, edgePath, visited);
          edgePath.pop(); // 回溯
        }
        path.pop(); // 回溯
      }
      // 对每个节点启动DFS，确保不遗漏
      for (const node in graph) {
          dfs(node, [], [], new Set());
      }
      return cycles;
    },
    /**
     * @description 校验存在死循环的规则 是否全部命中
     * @param {*} formData
     * @author caoy
     * @Date 2025-05-19 17:55:04
     */    
    checkRuleUseCycle(formData, cycleRuleList) {
      let flag = cycleRuleList.every(rule => {
        let result = judgeExpress(formData, rule.conditionList, rule.allCondition)
        return !!result
      })
      return flag;
    }
  },
  async mounted() {
/* ========================================================= */
    // 打印表单
    window.$wujie?.bus.$on('printForm', async ({ options, printFileName, isSign, signParams: [refOid, refOtype, signFileSuffix], wujieName }) => {
      if (wujieName === window.name) {
        // 获取签名组件，绑定它的手写签名图片在打印时显示
        const signRefLists = Object.values(this.widgetRefList).filter((item) => item.field?.type === 'sign-text');
        // 有签名组件组件时需要处理签名组件的打印态电子签名图片替换
        if (signRefLists.length) {
          // 打印配置勾选了电子签名
          if (isSign) {
            const res = await request({
              method: 'post',
              url: '/rest/v1/formData/getFormValueSignature',
              data: {
                refOid,
                refOtype,
                signFileSuffix
              }
            });
            if (res?.data?.data?.signContent) {
              const signConfig = JSON.parse(res.data.data.signContent);
              signRefLists.forEach((widget, i) => {
                const item = signConfig[widget.field.options.sign.signField];
                if (item) {
                  widget.setSignDataForPrint(item);
                }
              });
            }
          } else {
            // 打印配置没有勾电子签名
            signRefLists.forEach((widget) => widget.setSignDataForPrint());
          }
        }
        // 打印
        this.$nextTick(() => print(this.$refs.renderForm?.$el, options, printFileName));
      }
    });
    // 获取当前表单实例所有附件，版本变化时附件的fileId会变化，需要更新, 为确保刷新时附件已经实例化成功在此用￥watch添加监听
    if(this.isRefreshFileId){
      this.refershFileWatch.forEach((func) => typeof func === 'function' && func());
      this.refershFileWatch.push(
        this.$watch(
          'formData.curFormInstanceId',
          debounce(() => this.refreshFileId(), 500),
          { immediate: true }
        )
      );
    }
    // 表单渲染完之后的时间回调
    setTimeout(() => {
      window.$wujie?.bus.$emit('formRenderMounted', window.name);
    }, 500);
  },
  beforeUnmount() {
    [...this.watchArr, ...this.refershFileWatch, ...this.cascadWatchArr].forEach((func) => typeof func === 'function' && func());
  }
};
</script>

<style lang="scss" scoped>
.empty {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  height: 100%;
  min-height: 300px;
}
.no-data-img {
  display: block;
  margin: 0 auto;
}
.empty-text {
  color: #9da7af;
  font-size: 12px;
  margin-top: 12px;
  text-align: center;
}
</style>